<?php

namespace Muyuxuanpay\threePay\src\bft\protocolPayment;

use Muyuxuanpay\threePay\src\Exception;
use Muyuxuanpay\threePay\src\type;

trait ProtocolPaymentTrait
{
    protected function post($PostArry,$Req_Url,$analysis=true)
    {
        try {
            $postData = $PostArry;
            $postDataString = http_build_query($postData);//格式化参数
//            Log::EchoFormat("发送参数：".$Req_Url."?".$postDataString);
            $curl = curl_init(); // 启动一个CURL会话
            curl_setopt($curl, CURLOPT_URL, $Req_Url); // 要访问的地址
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
            //curl_setopt($curl, CURLOPT_SSLVERSION, 6); //CURL_SSLVERSION_DEFAULT (0), CURL_SSLVERSION_TLSv1 (1), CURL_SSLVERSION_SSLv2 (2), CURL_SSLVERSION_SSLv3 (3), CURL_SSLVERSION_TLSv1_0 (4), CURL_SSLVERSION_TLSv1_1 (5)， CURL_SSLVERSION_TLSv1_2 (6) 中的其中一个。
            curl_setopt($curl, CURLOPT_POST, true); //发送一个常规的Post请求
            curl_setopt($curl, CURLOPT_POSTFIELDS, $postDataString); // Post提交的数据包
            curl_setopt($curl, CURLOPT_TIMEOUT, 60); // 设置超时限制防止死循环返回
            curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
            $tmpInfo = curl_exec($curl); // 执行操作
            if (curl_errno($curl)) {
                $tmpInfo = curl_error($curl);//捕抓异常
            }
            curl_close($curl); // 关闭CURL会话
//            return $tmpInfo; // 返回数据
        } catch (\Exception $ex) {
//            echo $ex->getTraceAsString();
            throw new \Exception("请求异常：".$ex->getMessage());
        }
        if($analysis){
            parse_str($tmpInfo,$ReturnData);//参数解析
            if($this->VerifySign($ReturnData)){
                if($ReturnData['resp_code']=='F'||$ReturnData['resp_code']=='FF'){
                    $msg = $ReturnData['resp_msg']?$ReturnData['resp_msg']:($ReturnData['biz_resp_msg']?$ReturnData['biz_resp_msg']:'未知原因');
                    throw new \Exception("请求失败：".$msg);
                }
                return $ReturnData;
            }else{
                throw new \Exception("验签失败！");
            }
        }
        return $tmpInfo;
    }
    protected function decryptByCERFile($Data,$PublicPath)
    {
        try {
            if (!function_exists( 'hex2bin')) {
                throw new \Exception("hex2bin PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPublicKey($PublicPath);
            $DecryptRsult="";
            $blockSize = self::get_Key_Size($KeyObj,false);
            if($blockSize<=0){
                throw new \Exception("BlockSize is 0");
            }else{
                $blockSize = $blockSize/4;
            }
            $totalLen = strlen($Data);
            $EncryptSubStarLen = 0;
            $DecryptTempData="";
            while ($EncryptSubStarLen < $totalLen) {
                openssl_public_decrypt(hex2bin(substr($Data, $EncryptSubStarLen, $blockSize)), $DecryptTempData, $KeyObj);
                $DecryptRsult .= $DecryptTempData;
                $EncryptSubStarLen += $blockSize;
            }
            return base64_decode($DecryptRsult);
        } catch (\Exception $exc) {
//            echo $exc->getTraceAsString();
            throw new \Exception("解密异常：".$exc->getMessage());
        }
    }
    protected function VerifySign(array $data)
    {
        if(empty($data['signature'])){
            return true;
        }
        $sign = $data['signature'];
        unset($data['signature']);
        $signData = $this->SortAndOutString($data);
        $signData = $this->Sha1AndHex(urldecode($signData));
        if (!function_exists( 'hex2bin')) {
            function hex2bin( $str ) {
                $sbin = "";
                $len = strlen( $str );
                for ( $i = 0; $i < $len; $i += 2 ) {
                    $sbin .= pack( "H*", substr( $str, $i, 2 ) );
                }
                return $sbin;
            }
        }
        if(empty($CerPath)){
            $CerPath = $this->config['public_certs_path'];
        }
        if(!file_exists($CerPath)) {
            throw new \Exception("公钥文件不存在！路径：".$CerPath);
        }
        $PubKey = file_get_contents($CerPath);
        $Certs = openssl_get_publickey($PubKey);
        $ok = openssl_verify($signData,hex2bin($sign), $Certs);
        if ($ok == 1) {
            return true;
        }
        return false;
    }
    protected function Sign($Data,$PfxPath='',$Pwd='')
    {
        if(empty($PfxPath)){
            $PfxPath = $this->config['privite_certs_path'];
        }
        if(empty($Pwd)){
            $Pwd = $this->config['key'];
        }
        $Data = $this->SortAndOutString($Data);
        $Data = $this->Sha1AndHex(urldecode($Data));
        if (!function_exists( 'bin2hex')) {
            function hex2bin( $str ) {
                $sbin = "";
                $len = strlen( $str );
                for ( $i = 0; $i < $len; $i += 2 ) {
                    $sbin .= pack( "H*", substr( $str, $i, 2 ) );
                }
                return $sbin;
            }
        }
        if(!file_exists($PfxPath)) {
            throw new \Exception("私钥文件不存在！");
        }

        $pkcs12 = file_get_contents($PfxPath);
        $PfxPathStr=array();
        if (openssl_pkcs12_read($pkcs12, $PfxPathStr, $Pwd)) {
            $PrivateKey = $PfxPathStr['pkey'];
            $BinarySignature=NULL;
            if (openssl_sign($Data, $BinarySignature, $PrivateKey, OPENSSL_ALGO_SHA1)) {
                return bin2hex($BinarySignature);
            } else {
                throw new \Exception("加签异常！");
            }
        } else {
            throw new \Exception("私钥读取异常【密码和证书不匹配】！");
        }
    }
    protected function Sha1AndHex($data)
    {
        return openssl_digest($data, "SHA1");
    }

    /**
     * 排序输出k=v&k1=v1.....格式
     * @param array $data
     * @return string
     */
    protected function SortAndOutString(array $data)
    {
        $TempData = array();
        foreach ($data As $Key => $Value){
            if(!self::isBlank($Value)){
                $TempData[$Key] = $Value;
            }
        }
        ksort($TempData);//排序
        return http_build_query($TempData);
    }

    /**
     * 判断是否为空
     * @param $Strings
     * @return bool
     */
    protected static function isBlank($Strings)
    {
        $Strings = trim($Strings);
        if((empty($Strings)||($Strings == null))&&(strlen($Strings) <= 0)){
            return true;
        }else{
            return FALSE;
        }
    }
    /**
     * 公钥加密函数
     * @param string $Data 待加密数据
     * @param string $PublicPath 公钥文件路径
     * @return string
     * @throws \Exception
     */
    protected function encryptByCERFile($Data,$PublicPath)
    {
        if(is_array($Data)){
            $Data = json_encode($Data);
        }
        try {
            if (!function_exists( 'bin2hex')) {
                throw new \Exception("bin2hex PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPublicKey($PublicPath);
//            $is_base_64 = base64_decode($Data,true);
            if(!$this->isBase64($Data)){
                $BASE64EN_DATA = base64_encode($Data);
            }else{
                $BASE64EN_DATA = $Data;
            }
            $EncryptStr = "";
            $blockSize = self::get_Key_Size($KeyObj,false);
            if($blockSize<=0){
                throw new \Exception("BlockSize is 0");
            }else{
                $blockSize = $blockSize/8-11;
            }
            $totalLen = strlen($BASE64EN_DATA);
            $EncryptSubStarLen = 0;
            $EncryptTempData="";
            while ($EncryptSubStarLen < $totalLen){
                openssl_public_encrypt(substr($BASE64EN_DATA, $EncryptSubStarLen, $blockSize), $EncryptTempData, $KeyObj);
                $EncryptStr .= bin2hex($EncryptTempData);
                $EncryptSubStarLen += $blockSize;
            }
            return $EncryptStr;
        } catch (\Exception $exc) {
            throw new \Exception($exc->getMessage());
        }
    }
    protected function encryptByPFXFile($Data,$PfxPath,$PrivateKPASS)
    {
        try {
            if (!function_exists( 'bin2hex')) {
                throw new \Exception("bin2hex PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPriveKey($PfxPath,$PrivateKPASS);
//            echo "<pre>";
//            print_r($KeyObj);
//            exit;
            $BASE64EN_DATA = base64_encode($Data);
            $EncryptStr = "";
            $blockSize = self::get_Key_Size($KeyObj);
            if($blockSize<=0){
                throw new \Exception("BlockSize is 0");
            }else{
                $blockSize = $blockSize/8-11;//分段
            }
            $totalLen = strlen($BASE64EN_DATA);
            $EncryptSubStarLen = 0;
            $EncryptTempData="";
            while ($EncryptSubStarLen < $totalLen){
                openssl_private_encrypt(substr($BASE64EN_DATA, $EncryptSubStarLen, $blockSize), $EncryptTempData, $KeyObj);
                $EncryptStr .= bin2hex($EncryptTempData);
                $EncryptSubStarLen += $blockSize;
            }
            return $EncryptStr;
        } catch (\Exception $exc) {
//            echo $exc->getTraceAsString();
            throw new \Exception($exc->getMessage());
        }
    }
    /**
     * 读取私钥
     * @param type $PfxPath
     * @param type $PrivateKPASS
     * @return array
     * @throws Exception
     */
    private static function  getPriveKey($PfxPath,$PrivateKPASS){
        try {
            if(!file_exists($PfxPath)) {
                throw new \Exception("私钥文件不存在！路径：".$PfxPath);
            }
            $PKCS12 = file_get_contents($PfxPath);
            $PrivateKey = array();
            if(openssl_pkcs12_read($PKCS12, $PrivateKey, $PrivateKPASS)){
                return $PrivateKey["pkey"];
            }else{
                throw new \Exception("私钥证书读取出错！原因[证书或密码不匹配]，请检查本地证书相关信息。");
            }
        } catch (\Exception $ex) {
            $ex->getTrace();
        }
    }
    protected function isBase64($str)
    {
        return $str == base64_encode(base64_decode($str)) ? true : false;
    }
    protected function AesEncrypt($data,$key)
    {
        if(is_array($data)){
            $data = base64_decode($data);
        }
        if(!$this->isBase64($data)){
            $data = base64_encode($data);
        }

        if (!function_exists( 'bin2hex')) {
            function hex2bin( $str ) {
                $sbin = "";
                $len = strlen( $str );
                for ( $i = 0; $i < $len; $i += 2 ) {
                    $sbin .= pack( "H*", substr( $str, $i, 2 ) );
                }
                return $sbin;
            }
        }
        if(!(strlen($key) == 16)){
            throw new \Exception("AES密码长度固定为16位！当前KEY长度为：".  strlen($key));
        }
        $iv=$key;//偏移量与key相同
        //OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING
        $encrypted = openssl_encrypt($data, "AES-128-CBC", $key,OPENSSL_RAW_DATA,$iv);
        //$encrypted=mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key,$data,MCRYPT_MODE_CBC,$iv);
        $data=bin2hex($encrypted);
        return $data;
    }
    /**
     * 读取公钥
     * @param type $PublicPath
     * @return type
     * @throws Exception
     */
    protected static function  getPublicKey($PublicPath){
        try {
            if(!file_exists($PublicPath)) {
                throw new \Exception("公钥文件不存在！路径：".$PublicPath);
            }
            $KeyFile = file_get_contents($PublicPath);
            $PublicKey = openssl_get_publickey($KeyFile);
            if(empty($PublicKey)){
                throw new \Exception("公钥不可用！路径：".$PublicPath);
            }
            return $PublicKey;
        } catch (\Exception $ex) {
//            $ex->getTraceAsString();
            throw new \Exception($ex->getMessage());
        }
    }
    /**
     * 获取证书长度
     * @param type $Key_String
     * @param type $Key_Type
     * @return int
     * @throws Exception
     */
    protected static function get_Key_Size($Key_String,$Key_Type=true){
        $Key_Temp=array();
        try{
            if($Key_Type){//私钥
                $Key_Temp = openssl_pkey_get_details(openssl_pkey_get_private($Key_String));
            }else if(openssl_pkey_get_public($Key_String)){//公钥
                $Key_Temp = openssl_pkey_get_details(openssl_pkey_get_public($Key_String));
            }else{
                throw new \Exception("Is not a key");
            }
            IF(array_key_exists("bits",$Key_Temp)){
                return $Key_Temp["bits"];
            }else{
                return 0;
            }
        } catch (\Exception $ex){
            $ex->getTrace();
            throw new \Exception($ex->getMessage());
        }
    }
}